#include "FileTree.h"

using namespace std;

FileTree::FileTree(string cwPath, bool createTree,bool verbose)
{
	this->verbose=verbose;
	cout<<"FileTree verbose is: "<<this->verbose<<endl;
	this->workingPath=cwPath+"/tree/";
	if(createTree)
	{
		cout<<"Creating Tree ..."<<endl;
		if(this->initTree())
			cout<<"Created Tree in position: "<<this->workingPath<<endl;
		else
			cout<<"Failed to create Tree"<<endl;
	}
	else
		cout<<"Tree creation not requested!"<<endl;
}

FileTree::~FileTree()
{

}

/* questo metodo inizializza l'albero di cartelle pensato ad hoc per questo progetto */
bool FileTree::initTree()
{
	char d[256];
	string path;
	ofstream stream;
	for(int j=-WMAX;j<WMAX+1;j++)//x
	{
		for(int k=-HMAX;k<HMAX+1;k++)//y
		{
			for(int t=0;t<360;t+=90)//alpha
			{
				for(int l=0;l<2;l++)//bool
				{
					sprintf(d,"%d/%d/%d/%d/",j,k,t,l);
					path=string(this->workingPath+d);
					if(mkpath(path, 07777)!=0)
					{
						cout<<"FileTree->initTree - problem making dir path : "<<string(this->workingPath+d)<<endl;
						cout<<">>>>>>>>>> errno: "<<errno<<" - "<<strerror(errno)<<endl;
						return false;
					}
					else
					{
						//cout<<"dir: "<<string(this->workingPath+d)<<endl;
						for(int h=0;h<2;h++)//bool
						{
							sprintf(d,"%s%d.txt",path.c_str(),h);
							stream.open(d, fstream::out);
							if(stream.is_open())
							{
								stream<<0.0<<endl<<0<<endl<<0.0<<endl;//todo terzo valore bias, da inizializzare secondo distanze
								if(this->verbose)
									cout<<"In position: "<<path<<"\ninitiated file: "<<d<<"\n--------------"<<endl;
							}
							else
							{
								cout<<"PROBLEM making text file in FileTree->initTree!"<<endl;
								return false;
							}
							stream.close();
						}
					}
				}
			}
		}
	}
	return true;
}

bool FileTree::writeCumulatedReward(double value,int steps)
{
	string s=this->workingPath+"cumulatedReward.txt";
	fstream stream;
	stream.open(s.c_str(),fstream::app | fstream::out);
	if(stream.is_open())
	{
		stream<<value<<","<<steps<<endl;
		stream.close();
	}
	else
	{
		cout<<"ERROR opening file: "<<s<<endl;
		return false;
	}
	return true;
}

qGroup_t FileTree::getQgroupFromFile(qGroup_t group){

	group.su=getQitemFromFile(group.su);
	group.giu=getQitemFromFile(group.giu);
	group.sx=getQitemFromFile(group.sx);
	group.dex=getQitemFromFile(group.dex);

return group;
}

qFam_t FileTree::getQFamFromFile(qFam_t family){

	family.both=getQgroupFromFile(family.both);
	family.pink=getQgroupFromFile(family.pink);
	family.none=getQgroupFromFile(family.none);
	family.orange=getQgroupFromFile(family.orange);

return family;
}

void FileTree::writeDistance(int x, int y, double dist)
{
	char s[128];
	sprintf(s,"%d/%d/dis.txt",x,y);
	string path=this->workingPath+string(s);
	fstream fs;
	fs.open(path.c_str(),fstream::out);
	if(fs.is_open())
	{
		fs<<dist<<endl;
		fs.close();
	}
	else
	{
		cout<<"Error opening file: "<<path<<" in writeDistance"<<endl;
	}
}

double FileTree::getDistance(int x,int y)
{
	char s[128];
	sprintf(s,"%d/%d/dis.txt",x,y);
	string path=this->workingPath+string(s);
	fstream fs;
	fs.open(path.c_str(),fstream::in);
	if(fs.is_open())
	{
		fs.getline(s,128);
		return strtod(s,NULL);
	}
	else
	{
		cout<<"ERROR OPENING FILE: "<<path<<" in getDistace"<<endl;
		return -9999;
	}
}

/* da usare solo in fase di inizializzazione dei file! */
void FileTree::setBias(int x, int y, int alpha, double bias)
{
	char s[128];
	sprintf(s,"%d/%d/%d/",x,y,alpha);
	string path=this->workingPath+string(s);
	string app;
	fstream fs;
	for(int i=0;i<2;i++)
	{
		for(int j=0;j<2;j++)
		{
			sprintf(s,"%d/%d.txt",i,j);
			app=path+string(s);

			fs.open(app.c_str(),fstream::out);
//			bias-=0.5;
			if(fs.is_open())
			{
				fs<<0<<endl<<0<<endl<<bias<<endl;//funzionale, solo perchè stiamo in fase di init!
				fs.close();
			}
			else
			{
				cout<<"ERRORE ad aprire il file: "<<app<<endl;
				return;
			}
		}
	}
}

int FileTree::removeFile(int x, int y)
{
	char s[128];
	sprintf(s,"%d/%d/dis.txt",x,y);
	string path=this->workingPath+string(s);
	int err=remove(path.c_str());
	if( err != 0 )
	    cout<<"ERROR deleting file: "<<path<<endl;
	  else
		  if(this->verbose)
			  cout<< "File: "<<path<<" successfully deleted!"<<endl;
	return err;
}

void FileTree::setBiasOnTree()
{
	double dis,dis0,dis90,dis180,dis270,bias0,bias90,bias180,bias270;
	bool flag0=false,flag90=false,flag180=false,flag270=false;
	for(int i=-WMAX;i<WMAX+1;i++)//x
	{
		for(int j=-HMAX;j<HMAX+1;j++)//y
		{
			switch(i)
			{
			case (-WMAX):
				switch(j)
				{
				case -HMAX://in basso a sx
					//0
					flag0=true;
					//90
					flag90=true;
					break;
				case HMAX://in alto a sx
					//0
					flag0=true;
					//270
					flag270=true;
					break;
				case -HMAX+1 ... HMAX-1://bordo sx
					//0
					flag0=true;
					//90
					flag90=true;
					//270
					flag270=true;
					break;
				}
				break;
			case (WMAX):
				switch(j)
				{
				case -HMAX://in basso a dx
					//90
					flag90=true;
					//180
					flag180=true;
					break;
				case HMAX://alto dx
					//180
					flag180=true;
					//270
					flag270=true;
					break;
				case -HMAX+1 ... HMAX-1://bordo dx
					//90
					flag90=true;
					//180
					flag180=true;
					//270
					flag270=true;
					break;
				}
				break;
			case -WMAX+1 ... WMAX-1:
				switch(j)
				{
				case -HMAX://bordo giu
					//0
					flag0=true;
					//90
					flag90=true;
					//180
					flag180=true;
					break;
				case HMAX://bordo su
					//0
					flag0=true;
					//180
					flag180=true;
					//270
					flag270=true;
					break;
				case -HMAX+1 ... HMAX-1://matrice centrale
					//0
					flag0=true;
					//90
					flag90=true;
					//180
					flag180=true;
					//270
					flag270=true;
					break;
				}
				break;
			}

			dis=this->getDistance(i,j);
			//stampare valori bias!!!
			if(flag0)//0 i+1,j 90 i,j+1 180 i-1,j //270 i,j-1
			{
				dis0=this->getDistance(i+1,j);
				bias0=dis-dis0;
				this->setBias(i,j,0,bias0);
//				this->setBias(i,j,0,(14-dis)/14);
				//cout<<"distance"<<(14-dis)/14<<" a "<<i<<","<<j<<endl;
				flag0=false;
			}
			if(flag90)
			{
				dis90=this->getDistance(i,j+1);
				bias90=dis-dis90;
				this->setBias(i,j,90,bias90);
//				this->setBias(i,j,90,(14-dis)/14);
				//cout<<"distance"<<(14-dis)/14<<" a "<<i<<","<<j<<endl;
				flag90=false;
			}
			if(flag180)
			{
				dis180=this->getDistance(i-1,j);
				bias180=dis-dis180;
				this->setBias(i,j,180,bias180);
//				this->setBias(i,j,180,(14-dis)/14);
				//cout<<"distance"<<(14-dis)/14<<" a "<<i<<","<<j<<endl;
				flag180=false;
			}
			if(flag270)
			{
				dis270=this->getDistance(i,j-1);
				bias270=dis-dis270;
				this->setBias(i,j,270,bias270);
//				this->setBias(i,j,270,(14-dis)/14);
				//cout<<"distance"<<(14-dis)/14<<" a "<<i<<","<<j<<endl;
				flag270=false;
			}
		}
	}
}

/* questa funzione crea ricorsivamente le directory contenute in @path */
int FileTree::mkpath(string path, mode_t mode)
{
	char *file_path=(char*)path.c_str();
	assert(file_path && *file_path);
	char* p;
	for (p=strchr(file_path+1, '/'); p; p=strchr(p+1, '/'))
	{
		*p='\0';
		if (mkdir(file_path, mode)==-1)
		{
			if (errno!=EEXIST)
			{
				*p='/';
				return -1;
			}
		}
		*p='/';
	}
	return 0;
}

string FileTree::makePathFromItem(qItem_t item)
{
	stringstream ss;
	/* path: working path/x/y/angle/fl1/fl2.txt */
	ss<<this->workingPath<<item.state.x<<"/"<<item.state.y<<"/"<<item.angle<<"/"<<item.state.fl1<<"/"<<item.state.fl2<<".txt";
	return ss.str();
}

qItem_t FileTree::makeItemFromPath(string path)
{
	qItem_t item;
	/* path: working path/x/y/angle/fl1/fl2.txt */
	//path = string ( path.rbegin(), path.rend() );//reverse a string
	//cout<<path<<endl;
	size_t pos=path.find_last_of(".");
	path=path.substr(0,pos);
	string substr;
	if(pos!=string::npos)
	{
		pos=path.find_last_of("/");
		substr=path.substr(pos+1);
		path=path.substr(0,pos);
		//cout<<substr<<endl;
		item.state.fl2=atoi(substr.c_str());

		pos=path.find_last_of("/");
		substr=path.substr(pos+1);
		path=path.substr(0,pos);
		//cout<<substr<<endl;
		item.state.fl1=atoi(substr.c_str());

		pos=path.find_last_of("/");
		substr=path.substr(pos+1);
		path=path.substr(0,pos);
		//cout<<substr<<endl;
		item.angle=atoi(substr.c_str());

		pos=path.find_last_of("/");
		substr=path.substr(pos+1);
		//cout<<substr<<endl;
		path=path.substr(0,pos);
		item.state.y=atoi(substr.c_str());

		pos=path.find_last_of("/");
		substr=path.substr(pos+1);
		//cout<<substr<<endl;
		item.state.x=atoi(substr.c_str());
	}

	return item;
}

bool FileTree::isGoodQitem(qItem_t item)
{
	if(! External::isGoodAngle(item.angle))
	{
		cout<<"ERROR: angle wrong: "<<item.angle<<endl;
		return false;
	}
	if((item.state.x < -WMAX) || (item.state.x > WMAX) || (item.state.y < -HMAX) || (item.state.y > HMAX))
	{
		cout<<"ERROR: coordinate value out of margin:"<<item.state.x<<"-"<<item.state.y<<endl;
		return false;
	}
	return true;
}

bool FileTree::setQvalueOnFile(qItem_t item)
{
	if(!this->isGoodQitem(item))
	{
		return false;
	}
	string path=this->makePathFromItem(item);
	if(item.lv.qVal<-1)
		{
			item.lv.qVal=-1;
		}
	if(item.lv.qVal>1)
		{
			item.lv.qVal= 1;
		}
	fstream fs;
	fs.open(path.c_str(),fstream::out);
	if(fs.is_open())
	{
		item.lv.refreshCount++;
		fs<<item.lv.qVal<<endl<<item.lv.refreshCount<<endl<<item.lv.bias<<endl;
		fs.close();
	}else{
		cout<<"ERROR opening file: "<<path<<" in setQvalueOnFile!"<<endl;
		return false;
	}
	return true;

}
/*  ritorna un item con i campi di learning letti da file e gli altri uguali a quelli passati in input */
qItem_t FileTree::getQitemFromFile(qItem_t item)
{
	fstream fs;
	if(!this->isGoodQitem(item))
	{

	}
	qItem_t qIt=External::initQitem(qIt);
	qIt=External::copyQitem(item,qIt);
	string path=this->makePathFromItem(item);
	qIt=this->getQitemFromFile(path);
	/*

	fs.open(path.c_str(),fstream::in);

	if(fs.is_open())
	{
		fs.getline(app,256);
		qIt.lv.qVal=strtod(app,NULL);
		fs.getline(app,256);
		qIt.lv.refreshCount=strtod(app,NULL);
		fs.getline(app,256);
		qIt.lv.bias=strtod(app,NULL);
	}
	else
	{
		cout<<"failed to open file: "<<path<<" in getQitemFromFile(Item)!"<<endl;
	}
	fs.close();*/
	return qIt;
}

qItem_t FileTree::getQitemFromFile(string path)
{
	qItem_t qIt=External::initQitem(qIt);
	qIt=this->makeItemFromPath(path);
	char app[256];
	fstream fs;
	fs.open(path.c_str(),fstream::in);
	if(fs.is_open())
	{
		fs.getline(app,256);
		qIt.lv.qVal=strtod(app,NULL);
		fs.getline(app,256);
		qIt.lv.refreshCount=strtod(app,NULL);
		fs.getline(app,256);
		qIt.lv.bias=strtod(app,NULL);
		fs.close();
	}
	else
	{
		cout<<"problem to open file: "<<path<<" in getQitemFromFile(string)!"<<endl;
	}

	return qIt;
}
